<body></body>
<script>
  let activeEffect = null
  const effectStack = [] //effect栈
  const bucket = new WeakMap()

  const data = {
    foo: 1,
    bar: 2
  }

  const obj = new Proxy(data, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, newVal) {
      target[key] = newVal
      trigger(target, key)
    }
  })

  function track(target, key) {
    if (!activeEffect) return
    let depMap = bucket.get(target)
    if (!depMap) {
      bucket.set(target, (depMap = new Map()))
    }

    let deps = depMap.get(key)
    if (!deps) {
      depMap.set(key, (deps = new Set()))
    }
    const effectSet = deps.add(activeEffect)
    activeEffect.deps.push(effectSet)
  }

  function trigger(target, key) {
    let depMap = bucket.get(target)
    if (!depMap) return
    const depsSet = depMap.get(key)
    const effectsToRun = new Set(depsSet)
    effectsToRun &&
      effectsToRun.forEach(effectFn => {
        if (effectFn?.options?.scheduler) {
          effectFn.options.scheduler(effectFn) //执行调度
        } else {
          effectFn()
        }
      })
  }

  function effect(fn, options) {
    // 这里的 effectFn是一个独立函数，就是每一次effect开辟一个新的空间，使用的是新的指针
    function effectFn() {
      cleanup(effectFn)
      activeEffect = effectFn

      effectStack.push(effectFn)
      const res = fn()
      effectStack.pop(effectFn)
      activeEffect = effectStack[effectStack.length - 1]
      return res
    }
    effectFn.options = options
    effectFn.deps = [] //反向收集 effectSet的引用
    if (!effectFn?.options?.lazy) effectFn()
    return effectFn
  }
  function cleanup(effectFn) {
    effectFn.deps.forEach(depSet => {
      depSet.delete(effectFn) //遍历所有的 Set集合删除同一个副作用
    })
    effectFn.deps.length = 0
  }
  // 任务队列控制逻辑
  const jobQueue = new Set() // 任务队列（自动去重）
  let isFlushing = false // 队列刷新状态
  const p = Promise.resolve() // 微任务触发器

  function flushJob() {
    if (isFlushing) return
    isFlushing = true
    p.then(() => {
      jobQueue.forEach(job => job())
    }).finally(() => {
      isFlushing = false
      jobQueue.clear()
    })
  }

  // 计算属性：把 getter 作为副作用函数，创建一个 lazy 的 effect
  function computed(getter) {
    let value
    let dirty = true
    const effectFn = effect(getter, {
      lazy: true,
      scheduler() {
        if (!dirty) {
          dirty = true //添加调度器，在调度器中将dirty重置为true
          // 当计算属性依赖的响应式数据变化时，手动调用 trigger 函数触发响应
          trigger(obj, 'value') //执行value
        }
      }
    })

    const obj = {
      get value() {
        if (dirty) {
          value = effectFn()
          dirty = false
        }
        //当读取 value 时，手动调用track 函数进行追踪
        track(obj, 'value')
        return value
      }
    }
    return obj
  }

  //watch 函数接收两个参数，source 是响应式数据，cb是回调函数
  function watch(source, cb) {
    effect(
      // 触发读取操作，从而建立联系
      () => source.foo,
      //当数据变化时，调用回调函数cb
      {
        scheduler() {
          cb()
        }
      }
    )
  }
  // 修改响应数据的值，会导致回调函数执行
  watch(obj, () => {
    console.log('修改了')
  })
  obj.foo++
</script>
